package com.thunder.reader.parser;

import java.io.IOException;
import java.io.InputStream;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

import com.thunder.reader.FeedSettings;
import com.thunder.reader.data.Opml;
import com.thunder.reader.provider.Feed;

public class OpmlPullParser {
    private final String TAG = FeedSettings.GLOBAL_TAG;
    
    private InputStream mInputStream;
    private String mEncoding;
    
    private ContentResolver mContentResolver;
    private boolean mInterruptParsing;
    
    public OpmlPullParser(Context context, InputStream is) {
        mContentResolver = context.getContentResolver();
        mInputStream = is;
        mEncoding = FeedSettings.getDefaultEncoding();
        mInterruptParsing = false;
    }
    
    public OpmlPullParser(Context context, Uri sourceUri, InputStream is, String encoding) {
        this(context, is);
        mEncoding = encoding;
        if (mEncoding == null) {
            mEncoding = FeedSettings.getDefaultEncoding();
        }
    }
    
    public synchronized void parse() throws XmlPullParserException, IOException {
        assert(mInputStream == null);
        final XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
        factory.setNamespaceAware(true);
        final XmlPullParser parser = factory.newPullParser();
        
        parser.setInput(mInputStream, mEncoding);
        int eventType = parser.getEventType();
        if (eventType != XmlPullParser.START_DOCUMENT) {
            throw new XmlPullParserException("Not started with start_document, did you initialize the parser?");
        }
        eventType = parseOpml(parser);
        if (!mInterruptParsing && eventType != XmlPullParser.END_DOCUMENT) {
            throw new XmlPullParserException("Not ended with end_document, did you finish the parsing?");
        }
        if (mInputStream != null) {
            mInputStream.close();
        } else {
            Log.e(TAG, "inputstream is null, XmlPullParser closed it??");
        }
    }
    
    private int parseOpml(XmlPullParser parser) throws XmlPullParserException, IOException {
        int eventType = parser.getEventType();
        if (eventType != XmlPullParser.START_DOCUMENT) {
            throw new XmlPullParserException("not starting with 'start_document', is this a new document?");
        }
        Log.e(TAG, "starting document, are you aware of that!");
        eventType = parser.next();
        while (!mInterruptParsing && eventType != XmlPullParser.END_DOCUMENT) {
            switch (eventType) {
            case XmlPullParser.START_TAG: {
                final String tagName = parser.getName();
                if (tagName.equals(Opml.TAG_BODY)) {
                    parseBody(parser);
                }
                break;
            }
            default:
                break;
            }
            eventType = parser.next();
        }
        Log.e(TAG, "end of document, it is over");
        return parser.getEventType();
    }
    
    private int parseBody(XmlPullParser parser) throws XmlPullParserException, IOException {
        int eventType = parser.getEventType();
        if (eventType != XmlPullParser.START_TAG) {
            throw new XmlPullParserException("not starting with 'start_tag', is this a new tag body?");
        }
        Log.e(TAG, "start of tag body");
        int groupId = getDefaultGroupId();
        eventType = parser.nextTag();
        while (!mInterruptParsing && eventType == XmlPullParser.START_TAG && parser.getName().equals(Opml.TAG_OUTLINE)) {
            final String tagName = parser.getName();
//            Log.e(TAG, "start tag: '" + tagName + "'");
//            Log.e(TAG, "attr num " + parser.getAttributeCount());
            final String xmlUrl = parser.getAttributeValue(null, Opml.TAG_XML_URL);
//            Log.e(TAG, "attr xmlurl " + xmlUrl);
            final String htmlUrl = parser.getAttributeValue(null, Opml.TAG_HTML_URL);
//            Log.e(TAG, "attr htmlurl " + htmlUrl);
            final String title = parser.getAttributeValue(null, Opml.TAG_TITLE);
//            Log.e(TAG, "attr title " + title);
            final String description = parser.getAttributeValue(null, Opml.TAG_DESCRIPTION);
//            Log.e(TAG, "attr description " + description);
            final String text = parser.getAttributeValue(null, Opml.TAG_TEXT);
//            Log.e(TAG, "attr text " + text);
            if (TextUtils.isEmpty(xmlUrl)) {
                // This is a folder containing some feeds
                String t = title;
                if (TextUtils.isEmpty(t)) {
                    t = text;
                }
                String d = description;
                if (TextUtils.isEmpty(d)) {
                    d = t;
                }
                // query for this group to know whether it exists
                final Cursor c = mContentResolver.query(Feed.Group.CONTENT_URI, 
                        new String[]{Feed.Group._ID}, Feed.Group.TITLE + "=?", new String[]{t}, null);
                if (c != null && c.moveToFirst()) {
                    groupId = c.getInt(0);
                    c.close();
                } else {
                    if (c != null) {
                        c.close();
                    }
                    // No such a group existing, insert it into database
                    final ContentValues cv = new ContentValues();
                    cv.put(Feed.Group.TITLE, t);
                    cv.put(Feed.Group.DESCRIPTION, d);
                    final Uri  uri = mContentResolver.insert(Feed.Group.CONTENT_URI, cv);
                    groupId = Integer.valueOf(uri.getPathSegments().get(1));
                }
                
                eventType = parser.nextTag();
                continue;
            } else {
                // query to know whether this feed exists?
                final Cursor c = mContentResolver.query(Feed.Source.CONTENT_URI, 
                        new String[]{Feed.Source._ID}, Feed.Source.FEED_URL + "=?", new String[]{xmlUrl}, null);
                if (c != null && c.moveToFirst()) {
                    c.close();
                } else {
                    if (c != null) {
                        c.close();
                    }
                    final ContentValues cv = new ContentValues();
                    cv.put(Feed.Source.TITLE, title);
                    cv.put(Feed.Source.DESCRIPTION, description);
                    cv.put(Feed.Source.FEED_URL, xmlUrl);
                    cv.put(Feed.Source.URL, htmlUrl);
                    cv.put(Feed.Source.GROUP_ID, groupId);
                    mContentResolver.insert(Feed.Source.CONTENT_URI, cv);
                }
            }
            
            // Jump to next <outline> tag
            // TODO: should we use a while loop here?
            eventType = parser.nextTag();
            if (eventType == XmlPullParser.END_TAG) {
                eventType = parser.nextTag();
                if (eventType == XmlPullParser.END_TAG) {
                    eventType = parser.nextTag();
                }
            }
        }
//        Log.e(TAG, "end of body");
        return parser.getEventType();
    }
    
    private int getDefaultGroupId() {
        final Cursor cursor = mContentResolver.query(Feed.Group.CONTENT_URI, 
                new String[]{Feed.Group._ID}, Feed.Group.TITLE + "=?", 
                new String[]{Feed.Group.GROUP_DEFAULT}, null);
        int groupId = -1;
        if (cursor == null || !cursor.moveToFirst()) {
            // no default group, create one
            ContentValues values = new ContentValues();
            values.put(Feed.Group.TITLE, Feed.Group.GROUP_DEFAULT);
            values.put(Feed.Group.DESCRIPTION, Feed.Group.GROUP_DEFAULT);
            Uri uri = mContentResolver.insert(Feed.Group.CONTENT_URI, values);
            groupId = Integer.valueOf(uri.getPathSegments().get(1));
        }
        groupId = cursor.getInt(0);
        cursor.close();
        return groupId;
    }
}